/*
 *  KnowledgeBase.h
 *  IncrementalAlgorithmSerial
 *
 *  Created by Roman Kutlak on 27/03/2010.
 *  Copyright 2010 Roman Kutlak. All rights reserved.
 *
 */

#ifndef _KNOWLEDGEBASE_H_
#define _KNOWLEDGEBASE_H_

#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <vector>
#include <unordered_map>
#include <iterator>
#include <memory>

#include "Entity.h"
#include "Ontology.h"


//#define KNOWLEDGEBASE_DEBUG 0

namespace kutlak {
namespace roman {
namespace gre {
    
    class KnowledgeBase;
    
    typedef std::shared_ptr<KnowledgeBase> pKnowledgeBase;
    
    /*! \brief Iterator that returns Entity instead of pair<string, EntityPtrT>;
     *  The iterator takes a type parameter that allows the iterator to only 
     *  list entities of a particular type. In order to do that, the iterator
     *  has to have a pointer to the end of the container. This means that any
     *  insertion or deletion makes the iterator invalied and any behaviour
     *  of called functions is UNDEFINED!
    */
    class EntityIterator 
    {
    public:
        typedef std::unordered_map<std::string, EntityPtrT> container;
        typedef std::iterator_traits<container::const_iterator>::iterator_category iterator_category;
        typedef Entity value_type;
        typedef size_t size_type;
        typedef std::iterator_traits<container::const_iterator>::difference_type difference_type;
        typedef Entity* pointer;
        typedef const Entity* const_pointer;
        typedef Entity& reference;
        typedef const Entity& const_reference;
        
        EntityIterator() {}
        
        EntityIterator(const Property& type, 
                       container::iterator iter,
                       container::iterator end):
        mIt(iter) , mEnd(end), mType(type) 
        {
            // if the first element is not the required type
            if (mIt != mEnd && !mIt->second->IsType(mType))
            {
                ++(*this); // move ahead
            }
        }
        
        EntityIterator(const EntityIterator& from) :
        mIt(from.mIt), mEnd(from.mEnd), mType(from.mType) {}
        
        EntityIterator& operator=(EntityIterator from) 
        {
            mIt = from.mIt;
            mEnd = from.mEnd;
            mType = from.mType;
            return *this;
        }
        
        Entity& operator*() 
        {
            return *(mIt->second);
        }
        
        Entity* operator->() 
        {
            return &(*(mIt->second));
        }
        
        EntityIterator& operator++() 
        {
            // skip instances of different type
            while (mIt != mEnd)
            {
                ++mIt;
                
                if (mIt == mEnd || mIt->second->IsType(mType))
                    break;
            }
            
            return *this;
        }
        
        EntityIterator operator++(int) 
        {
            EntityIterator tmp(*this);
            ++(*this); // use the above operator
            return tmp;
        }
        
        bool operator==(const EntityIterator& other) const 
        {
            return mIt == other.mIt;
        }
        
        bool operator!=(const EntityIterator& other) const 
        {
            return !(*this == other);
        }
        
    private:
        container::iterator mIt;
        container::iterator mEnd;
        Property mType;
    };
    
    
    //! KnowledgeBase is a container class for entities.
    /*! The class KnowledgeBase serves as a container for entities. It is
     *  a base class that can be extended to manipulate different sources
     *  such as rdf file with n-triples or redland library that accesses
     *  a mysql database containing RDF graph.
     */
    class KnowledgeBase 
    {
    public:
        // define types of container and iterators
        /*! \typedef ContainerT is the type of container the KnowledgeBase class uses to 
         *  store the entites. It should be a map-style ADT.
         */
        
        typedef std::unordered_map<std::string, EntityPtrT> ContainerT;
        
        //! \typedef iterator type
        typedef ContainerT::iterator iterator;
        //! \typedef const iterator type
        typedef ContainerT::const_iterator const_iterator;
        //! \typedef value type (i.e. the template parameters to container).
        typedef ContainerT::value_type value_type;
        
        
#pragma mark -
#pragma mark lifecycle
        //! The default constructor.
        KnowledgeBase(const std::string& filename = "");
        
        KnowledgeBase(const std::vector<EntityPtrT>& entities);
        
        KnowledgeBase(const KnowledgeBase& src);
        
        KnowledgeBase(KnowledgeBase&& src) noexcept(true);
        
        //! The assignment operator.
        KnowledgeBase& operator=(KnowledgeBase rhs) noexcept(true);
        
        friend void swap(KnowledgeBase& lhs, KnowledgeBase& rhs) noexcept(true);
        
        pKnowledgeBase Clone(pKnowledgeBase);// TODO
        
        virtual ~KnowledgeBase() noexcept(true);
        
#pragma mark -
#pragma mark operators
        
        //! The operator compares two KnowledgeBases.
        /*! The equality operator returns true if the two compared KnowledgeBases
         have the same set of Entities.
         */
        bool operator==(const KnowledgeBase& rhs) const;
        bool operator!=(const KnowledgeBase& rhs) const;
        
#pragma mark -
#pragma mark accessors
        // do we really want to allow modifying the object from outside?
        // maybe we should only provide const iterator
        
        //! Returns the iterator to the first entity
        iterator        begin()        { return mEntities.begin(); }
        
        //! Returns the const iterator to the first entity
        const_iterator  begin() const  { return mEntities.begin(); }
        
        //! Returns the iterator behind the last entity
        iterator        end()          { return mEntities.end(); }
        
        //! Returns the const iterator behind the last entity
        const_iterator  end()   const  { return mEntities.end(); }
        
        EntityIterator 
        EntitiesBegin(const Property& type = Ontology::kThingType) {
            return EntityIterator(type, mEntities.begin(), mEntities.end()); 
        }
        
        EntityIterator 
        EntitiesEnd() { 
            return EntityIterator(Ontology::kThingType,
                                  mEntities.end(), mEntities.end());
        }
        
        //! Returns the number of entities in the KnowledgeBase.
        virtual size_t Size() const;
        
        //! Remove all entities.
        virtual void Clear();
        
        //! Returns a copy of the entities held by this KnowledgeBase.
        virtual std::vector<EntityPtrT> Entities() const;
        
        //! Returns entities of speficied type (including subclasses).
        virtual std::vector<EntityPtrT> EntitiesOfType(const Property& type) const;
        
        //! Returns a copy of the entities held by this KnowledgeBase.
        /*! The method can restrict the set of returned entities using
         a property as a filter. Entities that do not have the property
         will not be returned.
         \param pattern Property that serves as a filter.
         \return container with entities available in the KnowledgeBase having the supplied property.
         */
        virtual std::vector<EntityPtrT> Entities(const Property& pattern) const ;
        
        //! Adds an entity to the KnowledgeBase.
        virtual void AddEntity(const Entity& entity);
        
        //! Removes an entity from the KnowledgeBase.                
        virtual void RemoveEntity(const Entity& entity);
        
        //! Returns the pointer to the Entity with given Id or null pointer.
        /*! The method returns the pointer in a smart pointer container
         *  to avoid any memory leaks.
         \param entityId id of the entity being looked up
         \return a copy of the smart pointer that effectively points to the Entity.
         Note that if you don't want to change the object you need to create a copy.
         */
        virtual EntityPtrT EntityPtrById(int entityId) const;
        
        //! Returns the pointer to the Entity with given name or null pointer.                
        virtual EntityPtrT EntityPtrByName(const std::string& name) const;
        
        //! Prints the KnowledgeBase into the standard output.
        virtual void Print(int number = -1) const;
        
#pragma mark -
#pragma mark I/O
        
        
        friend std::ostream& operator<<(std::ostream& os,
                                        const KnowledgeBase& obj);
        
        friend std::istream& operator>>(std::istream& is,
                                        KnowledgeBase& obj);
        
        void Serialise(std::ostream& os) const;
        
        void Deserialise(std::istream& is);
        
        //! Reads a file into the KnowledgeBase.
        /*! This is an abstract method that has to be implemented by subclasses.
         KnowledgeBases that access database can pass a database connection
         string instead of a name of a file.
         \param fileName path to the file with a KnowledgeBase
         \param fileType the type of the file to pars - RDF n-triple or haskell list
         \return number of entities added to the KnowledgeBase
         */
        virtual void AddFile(const std::string& fileName, bool createNewEntities = true);
        
        //! Reads a file into the KnowledgeBase.
        /*! The method does not handle any exceptions. These should be 
         * handeled by the caller.
         \param fileName path to the file with a KnowledgeBase
         \param fileType the type of the file to pars - RDF n-triple or haskell list
         \return number of entities added to the KnowledgeBase
         */
        virtual void FromRdf(std::istream& input, bool createNewEntities = true);
        
        //! Saves the KnowledgeBase into a file.
        //! \param fileName the name of the file along with it's path.
        virtual void SaveFile(const std::string& fileName) const;
        
        //! Saves the KnowledgeBase into an output stream
        /*! The method does not handle any ostream exceptions. These
         *  should be handled by the caller.
         \param output outputstream into which the KnowledgeBase will be written.
         */
        virtual void ToRdf(std::ostream& output) const;
        
#pragma mark -
#pragma mark statics
    public:
        static void SetDefault(const pKnowledgeBase& kb);
        
        static pKnowledgeBase Default();
        
        
    protected:
        ContainerT mEntities; //!< The collection of entities.
        
        static pKnowledgeBase sDefault;
    };
    
    
    
    struct PairEqPred 
    {
        PairEqPred(const std::pair<std::string, EntityPtrT>& obj) : obj_(obj) {}
        
        bool operator() (const std::pair<std::string, EntityPtrT>& rhs) const 
        {
            return (obj_.first == rhs.first && (*obj_.second == *rhs.second) ); 
        }
        
        std::pair<std::string, EntityPtrT> obj_;
    };
}
}
}
#endif
